package fun.parser.ast.node.base;

import java.io.PrintStream;
import java.util.Enumeration;

import javax.swing.tree.TreeNode;

import fun.parser.lexer.Token;
import fun.parser.util.TokenUtils;
import fun.util.Evaluable;

public abstract class AstNode implements TreeNode, Evaluable {
	private static final char INDENT_CHAR = ' ';
	private static final AstNode[] EMPTY_ARRAY = new AstNode[0];
	// ==========================================================
	protected Token token = null;
	protected AstNode parent = null;
	protected AstNode[] children = EMPTY_ARRAY;

	protected AstNode(Token node) {
		this.token = node;
	}

	protected AstNode() {
		this(null);
	}

	public void setParent(AstNode parent) {
		this.parent = parent;
	}

	public AstNode getParent() {
		return parent;
	}

	public Token getToken() {
		return this.token;
	}

	// ===========================
	protected void setChildren(AstNode[] children) {
		if (null == children)
			children = EMPTY_ARRAY;
		else
			this.children = children;
	}

	public Enumeration children() {
		return new Enumeration() {
			private int index = 0;

			public boolean hasMoreElements() {
				return index < children.length;
			}

			public Object nextElement() {
				return children[index];
			}
		};
	}

	public boolean getAllowsChildren() {
		return true;
	}

	public TreeNode getChildAt(int childIndex) {
		return this.children[childIndex];
	}

	public int getChildCount() {
		return this.children.length;
	}

	public int getIndex(TreeNode node) {
		if (node instanceof AstNode) {
			AstNode n = (AstNode) node;
			for (int i = 0; i < this.children.length; i++)
				if (n == this.children[i])
					return i;
		}
		return 0;
	}

	public boolean isLeaf() {
		return 0 == this.getChildCount();
	}

	// ��JTree����ʾ�ı�ǩ
	public String toString() {
		if (null == this.token)
			return "";
		return token.getImage();
	}

	/**
	 * �ݹ�Ĵ�ӡ��״�ṹ��Ĭ������Ϊ4
	 * 
	 * @param out
	 */
	public void print(PrintStream out) {
		this.print0(out, 0, 4);
	}

	public void print(PrintStream out, int indent) {
		this.print0(out, 0, indent);
	}

	private void print0(PrintStream out, int indent, int step) {
	printIndent(out, indent);
	if (null == this.token)
	    out.println("null");
	else if (true == TokenUtils.isOperator(token)) {
	    // this is an operator node
	    out.println(token.getImage());
	    for (AstNode node : children)
		node.print0(out, indent + step, step);
	} else {
	    // this is an operator node
	    out.println(token.getImage());
	}
    }

	private void printIndent(PrintStream out, int n) {
		for (int i = 0; i < n; i++)
			out.print(INDENT_CHAR);
	}
}
